/*
 * @(#)XworkInterceptorMapping.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.core.xwork;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.nhncorp.lucy.common.util.SpringObjectFactory;

import com.nhncorp.usf.core.config.InterceptorInfo;
import com.nhncorp.usf.core.config.InterceptorStackInfo;
import com.nhncorp.usf.core.config.runtime.Action;
import com.nhncorp.usf.core.config.runtime.RuntimeInterceptorConfig;
import com.nhncorp.usf.core.interceptor.impl.InterceptorPropertyAware;
import com.nhncorp.usf.core.util.OgnlUtil;
import com.nhncorp.usf.core.util.StringUtil;

import com.opensymphony.xwork.interceptor.Interceptor;

/**
 * return interceptor ids.
 *
 * @author Web Platform Development Team
 */
public class XworkInterceptorMapping {
    static Log log = LogFactory.getLog(XworkInterceptorMapping.class);
    static final InterceptorInfo INTERCEPTOR_INFO = InterceptorInfo.getInstance();
    static List<RuntimeInterceptorConfig> defaultInterceptorConfig;

    private static XworkInterceptorMapping instance;

    /**
     * Gets the single instance of XworkInterceptorMapping.
     *
     * @return single instance of XworkInterceptorMapping
     */
    public static synchronized XworkInterceptorMapping getInstance() {
        if (instance == null) {
            instance = new XworkInterceptorMapping();
        }

        return instance;
    }

    /**
     * interceptor 이름에 해당하는 {@link Interceptor} {@link List} 반환.
     * 
     * @param actionInfo the Action
     * @param dataMap the data map
     * @return the interceptor list
     * @throws Exception the Exception
     */
    public List<Interceptor> getInterceptors(Action actionInfo, Map<String, Object> dataMap) throws Exception {

        List<RuntimeInterceptorConfig> interceptorConfigs = new ArrayList<RuntimeInterceptorConfig>();
        List<Interceptor> interceptors = new ArrayList<Interceptor>();

        // 하위 호환성을 위해 추가
        String simpleInterceptorRefs = actionInfo.getInterceptor();
        log.debug("simple interceptor Refs : " + simpleInterceptorRefs);

        if (simpleInterceptorRefs != null && simpleInterceptorRefs.trim().length() > 0) {
            simpleInterceptorRefs = StringUtil.deleteWhitespace(simpleInterceptorRefs);

            for (String interceptorId : simpleInterceptorRefs.split(",")) {
                extractInterceptors(interceptorId.trim(), interceptorConfigs);
            }

        }

        List<RuntimeInterceptorConfig> interceptorsInAction = actionInfo.getRuntimeInterceptors();

        if (interceptorsInAction != null && !interceptorsInAction.isEmpty()) {

            for (RuntimeInterceptorConfig config : actionInfo.getRuntimeInterceptors()) {
                extractInterceptors(config, interceptorConfigs);
            }

        }

        if (interceptorConfigs.isEmpty()) {
            String defaultStack = INTERCEPTOR_INFO.getDefaultInterceptorRef();

            if (defaultStack != null && defaultStack.length() > 0) {
                if (defaultInterceptorConfig == null) {
                    extractInterceptors(defaultStack, interceptorConfigs);
                    defaultInterceptorConfig = interceptorConfigs;
                }

                interceptorConfigs = defaultInterceptorConfig;
            }

        }

        // extract all interceptors
        for (RuntimeInterceptorConfig config : interceptorConfigs) {
            String clazz = INTERCEPTOR_INFO.getInterceptorClass(config.getName());
            Interceptor interceptor = createInterceptor(clazz, config.getParams(), dataMap);
            interceptors.add(interceptor);
        }

        return interceptors;
    }

    /**
     * Creates the interceptor.
     *
     * @param clazz         the clazz
     * @param paramConfigs  the param configs
     * @param dataStructure the data structure
     * @return the interceptor
     * @throws Exception the exception
     */
    protected Interceptor createInterceptor(String clazz, Map<String, String> paramConfigs,
                                            Map<String, Object> dataStructure) throws Exception {
        Interceptor interceptor;
        try {
            interceptor = SpringObjectFactory.getInstance().getObject(clazz);
        } catch (Exception except) {
            throw new IllegalArgumentException("failure in getting object(" + clazz + ")");
        }
        if (paramConfigs == null || paramConfigs.isEmpty()) {
            return interceptor;
        }

        setProperties(interceptor, paramConfigs, dataStructure);
        return interceptor;
    }

    /**
     * Sets the properties.
     *
     * @param interceptor   the interceptor
     * @param paramConfigs  the param configs
     * @param dataStructure the data structure
     * @throws Exception the exception
     */
    private void setProperties(Interceptor interceptor, Map<String, String> paramConfigs,
                               Map<String, Object> dataStructure) throws Exception {
        Properties properties = new Properties();
        boolean useOgnl = interceptor instanceof InterceptorPropertyAware ? false : true;

        if (useOgnl) {
            for (String name : paramConfigs.keySet()) {
                if (name == null || name.length() == 0) {
                    continue;
                }

                String expression = paramConfigs.get(name);

                if (expression != null) {
                    properties.put(name, OgnlUtil.evaluateAndGet(expression, dataStructure));
                } else {
                    log.error("name[" + name + "], expression[" + expression + "]");
                }
            }

            com.opensymphony.xwork.util.OgnlUtil.setProperties(properties, interceptor);
        } else {
            for (String name : paramConfigs.keySet()) {
                if (name == null || name.length() == 0) {
                    continue;
                }

                String value = paramConfigs.get(name);
                properties.put(name, value);
            }

            ((InterceptorPropertyAware)interceptor).setProperties(properties);
        }
    }

    /**
     * recursively extract id of interceptor.
     *
     * @param interceptorName the interceptor name
     * @param configs the RuntimeInterceptorConfig list
     */
    private void extractInterceptors(String interceptorName,
                                     List<RuntimeInterceptorConfig> configs) {
        InterceptorStackInfo interceprotsStackInfo = INTERCEPTOR_INFO.getInterceptorStackInfo(interceptorName);

        if (interceprotsStackInfo == null) {
            if (alreadyExist(interceptorName, configs)) {
                log.info("'" + interceptorName + "' is alreay existed interceptor."
                        + "It may occur infinite reference.");
            }

            configs.add(new RuntimeInterceptorConfig(interceptorName));
        } else {
            for (String subInterceptorId : interceprotsStackInfo.getInterceptorRefs()) {
                extractInterceptors(subInterceptorId, configs);
            }
        }
    }

    /**
     * Extract interceptors.
     *
     * @param interceptorConfig         the interceptor config
     * @param runtimeInterceptorConfigs the runtime interceptor configs
     */
    private void extractInterceptors(RuntimeInterceptorConfig interceptorConfig,
                                     List<RuntimeInterceptorConfig> runtimeInterceptorConfigs) {
        String interceptorName = interceptorConfig.getName();
        InterceptorStackInfo interceprotsStackInfo = INTERCEPTOR_INFO.getInterceptorStackInfo(interceptorName);

        if (interceprotsStackInfo == null) {
            if (alreadyExist(interceptorConfig.getName(), runtimeInterceptorConfigs)) {
                log.info("'" + interceptorName + "' is alreay existed interceptor."
                        + "It may occur infinite reference.");
            }

            runtimeInterceptorConfigs.add(interceptorConfig);
        } else {
            for (String subInterceptorId : interceprotsStackInfo.getInterceptorRefs()) {
                extractInterceptors(subInterceptorId, runtimeInterceptorConfigs);
            }
        }
    }

    /**
     * Already exist.
     *
     * @param name    the name
     * @param configs the configs
     * @return true, if successful
     */
    private boolean alreadyExist(String name, List<RuntimeInterceptorConfig> configs) {
        for (RuntimeInterceptorConfig config : configs) {
            if (config.getName() == null) {
                continue;
            }
            if (config.getName().equals(name)) {
                return true;
            }
        }

        return false;
    }
}
